// Copyright (c) 2023 Huawei Device Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::{structure::*, XmlError};
use core::cell::RefCell;
use std::{
    cell::{Ref, RefMut},
    rc::Rc,
};

/// `Text` structure, which corresponds to `Interface Text`.
///
/// # Association
/// [Interface Text](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1312295772)
///
/// # Example
/// ```
/// use ylong_xml::{Document, DocumentMethod, ValuedNodeMethod};
///
/// let document = Document::new();
/// let text = document.create_text("Text").unwrap();
/// assert_eq!(text.value(), String::from("Text"));
/// ```
#[derive(Debug, Clone)]
pub struct Text {
    pub(crate) inner: Rc<RefCell<TextInner>>,
}

impl Text {
    /// Create a `Text` with the given value and its owner document.
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, Text, ValuedNodeMethod};
    ///
    /// let document = Document::new();
    /// let text = Text::new("Text", &document).unwrap();
    /// assert_eq!(text.value(), String::from("Text"));
    /// ```
    pub fn new(value: &str, owner_document: &Document) -> Result<Self, XmlError> {
        Ok(Self {
            inner: Rc::new(RefCell::new(TextInner::new(
                value.to_string(),
                Some(owner_document.downgrade()),
            ))),
        })
    }

    /// Create an `Text` with child members.
    pub(crate) fn new_unchecked(value: String, owner_document: Option<WeakDocument>) -> Self {
        Self {
            inner: Rc::new(RefCell::new(TextInner::new(value, owner_document))),
        }
    }
}

impl NodeMethod for Text {
    fn as_node(&self) -> Node {
        Node::Text(self.clone())
    }

    fn name(&self) -> String {
        String::from("#text")
    }

    fn node_type(&self) -> NodeType {
        NodeType::Text
    }

    fn owner_document(&self) -> Option<Document> {
        self.inner.borrow().owner_document.clone()?.upgrade()
    }
}

impl ValuedNodeMethod for Text {
    fn value(&self) -> String {
        self.inner.borrow().val.clone()
    }

    fn set_value(&mut self, value: &str) {
        self.inner.borrow_mut().val = value.to_string();
    }
}

impl ChildNodeInternalMethod for Text {
    fn set_parent(&mut self, parent: Option<WeakParentNode>) {
        self.inner.borrow_mut().parent = parent;
    }

    fn is_same_child_node(&self, node: &ChildNode) -> bool {
        node.as_text().map(|n| n == self).unwrap_or(false)
    }
}

impl ChildNodeMethod for Text {
    fn as_child_node(&self) -> ChildNode {
        ChildNode::Text(self.clone())
    }

    fn parent(&self) -> Option<ParentNode> {
        self.inner.borrow().parent.clone()?.upgrade()
    }
}

impl CharacterDataBorrowMethod for Text {
    type Inner = TextInner;

    fn borrow(&self) -> Ref<'_, Self::Inner> {
        self.inner.borrow()
    }

    fn borrow_mut(&self) -> RefMut<'_, Self::Inner> {
        self.inner.borrow_mut()
    }
}

impl PartialEq for Text {
    fn eq(&self, other: &Self) -> bool {
        self.inner.as_ptr() == other.inner.as_ptr()
    }
}

#[derive(Debug, Clone)]
pub(crate) struct TextInner {
    pub(crate) val: String,
    pub(crate) parent: Option<WeakParentNode>,
    pub(crate) owner_document: Option<WeakDocument>,
}

impl TextInner {
    pub(crate) fn new(val: String, owner_document: Option<WeakDocument>) -> Self {
        Self {
            val,
            parent: None,
            owner_document,
        }
    }
}

impl CharacterDataInternalMethod for TextInner {
    fn get_data(&self) -> &String {
        &self.val
    }

    fn get_data_mut(&mut self) -> &mut String {
        &mut self.val
    }
}

#[cfg(test)]
mod ut_text {
    use crate::structure::text::TextInner;
    use crate::structure::*;

    /*
     * @title  UT test for text borrow function
     * @design Condition Coverage
     * @precon NA
     * @brief  1. Call `new()` method to create a text.
     *         2. Call borrow get data。
     *         3. Check if borrow val is "Text"
     * @expect 1. true。
     * @auto   yes
     */
    #[test]
    fn ut_text_borrow() {
        let document = Document::new();
        let text = Text::new("Text", &document).unwrap();
        let text_inner = text.borrow();
        assert_eq!(text_inner.val, "Text");

        let text_mut = Text::new("mut Text", &document).unwrap();
        let text_inner = text_mut.borrow_mut();
        assert_eq!(text_inner.val, "mut Text");
    }

    /*
     * @title  UT test for TextInner function
     * @design Condition Coverage
     * @precon NA
     * @brief  1. Call TextInner `new()` method to get text.
     *         2. Call text get data。
     *         3. Comapre result
     * @expect 1. true。
     * @auto   yes
     */
    #[test]
    fn ut_text_inner() {
        let document = Document::new();
        let weak_document = document.downgrade();
        let text = TextInner::new("Text".to_string(), Some(weak_document));
        let text_inner_val = text.get_data();
        assert_eq!(text_inner_val, "Text");

        let document = Document::new();
        let weak_document = document.downgrade();
        let mut text = TextInner::new("Text".to_string(), Some(weak_document));
        let text_inner_val = text.get_data_mut();
        assert_eq!(text_inner_val, "Text");
    }

    /// UT test case for `Text::new()`.
    ///
    /// # Brief
    /// 1. Call `new()` method to create a `Text`.
    /// 2. Check if parts of the `Text` are default values.
    #[test]
    fn ut_text_new() {
        let document = Document::new();
        let text = Text::new("Text", &document).unwrap();
        let text_inner = text.inner.borrow();
        assert_eq!(text_inner.val, "Text");
        assert_eq!(text_inner.parent, None);
        assert_eq!(text_inner.owner_document, Some(document.downgrade()));
    }

    /// UT test case for `Text::as_node()`.
    ///
    /// # Brief
    /// 1. Create a `Text` called `text`.
    /// 2. Call `as_node()` method to convert `text` to `Node`.
    /// 3. Check if `Node::Text(text)` is obtained.
    #[test]
    fn ut_text_as_node() {
        let document = Document::new();
        let text = document.create_text("Text").unwrap();
        let node = text.as_node();
        assert_eq!(node, Node::Text(text));
    }

    /// UT test case for `Text::name()`.
    ///
    /// # Brief
    /// 1. Create a `Text` called `text`.
    /// 2. Call `name()` method to obtain `text`'s name.
    /// 3. Check if the obtained name is the given name.
    #[test]
    fn ut_text_name() {
        let document = Document::new();
        let text = document.create_text("Text").unwrap();
        assert_eq!(text.name(), "#text");
    }

    /// UT test case for `Text::node_type()`.
    ///
    /// # Brief
    /// 1. Create a `Text` called `text`.
    /// 2. Call `node_type()` method to obtain `text`'s type.
    /// 3. Check if this obtained node type is `NodeType::Text`.
    #[test]
    fn ut_text_node_type() {
        let document = Document::new();
        let text = document.create_text("Text").unwrap();
        assert_eq!(text.node_type(), NodeType::Text);
    }

    /// UT test case for `Text::owner_document()`.
    ///
    /// # Brief
    /// 1. Create a `Document` called `document`.
    /// 2. Create an `Text` of `document`, which is called `text`.
    /// 3. Call `owner_document()` method to obtain `text`'s owner document.
    /// 4. Check if `text`'s owner document is `Some(document)`.
    #[test]
    fn ut_text_owner_document() {
        let document = Document::new();
        let text = document.create_text("Text").unwrap();
        assert_eq!(text.owner_document(), Some(document));
    }

    /// UT test case for `Text::value()`.
    ///
    /// # Brief
    /// 1. Create a `Text` called `text` whose value is set to `"Text"`.
    /// 2. Call `value()` method to obtain `text`'s value.
    /// 3. Check if the value is equal to `"Text"`.
    #[test]
    fn ut_text_value() {
        let document = Document::new();
        let text = document.create_text("Text").unwrap();
        assert_eq!(text.value(), "Text");
    }

    /// UT test case for `Text::set_value()`.
    ///
    /// # Brief
    /// 1. Create a `Text` called `text` whose value is set to `"OldText"`.
    /// 2. Call `set_value()` method to set the value of `text` to `"NewText"`.
    /// 3. Get the value of `text` and check if it is equal to `"NewText"`.
    #[test]
    fn ut_text_set_value() {
        let document = Document::new();
        let mut text = document.create_text("OldText").unwrap();
        text.set_value("NewText");
        assert_eq!(text.value(), "NewText");
    }

    /// UT test case for `Text::set_parent()`.
    ///
    /// # Brief
    /// 1. Create a `Document` called `document`.
    /// 2. Create a `Text` called `text`.
    /// 3. Call `set_parent()` method to set `text`'s parent to `document`.
    /// 4. Get the parent of `text` and check if it is the same as `document`.
    #[test]
    fn ut_text_set_parent() {
        let document = Document::new();
        let mut text = document.create_text("Text").unwrap();
        text.set_parent(Some(document.as_parent_node().downgrade()));
        assert_eq!(text.parent(), Some(document.as_parent_node()));
    }

    /// UT test case for `Text::is_same_child_node()`.
    ///
    /// # Brief
    /// 1. Create two `Text`s called `text1` and `text2`.
    /// 2. Call `as_node()` method to convert these two `Text`s into
    /// `child1` and `child2` respectively.
    /// 3. Call `is_same_child_node` method to check if `text1` is the
    /// same as `child1`.
    /// 4. Call `is_same_child_node` method to check if `text1` is the
    /// same as `child2`.
    #[test]
    fn ut_text_is_same_child_node() {
        let document = Document::new();
        let text1 = document.create_text("Text").unwrap();
        let child1 = text1.as_child_node();
        let text2 = document.create_text("Text").unwrap();
        let child2 = text2.as_child_node();

        assert!(text1.is_same_child_node(&child1));
        assert!(!text1.is_same_child_node(&child2));
    }

    /// UT test case for `Text::as_child_node()`.
    ///
    /// # Brief
    /// 1. Create a `Text` called `text`.
    /// 2. Call `as_child_node()` method to convert `text` to `ChildNode`.
    /// 3. Check if `ChildNode::Text(comment)` is obtained.
    #[test]
    fn ut_text_as_child_node() {
        let document = Document::new();
        let text = document.create_text("Text").unwrap();
        let child_node = text.as_child_node();
        assert_eq!(child_node, ChildNode::Text(text));
    }

    /// UT test case for `Text::parent()`.
    ///
    /// # Brief
    /// 1. Create a `Element` called `element`.
    /// 2. Create a `Text` called `text`.
    /// 3. Call `parent()` method to check if `text` has no parent.
    /// 4. Set `text`'s parent to `element`.
    /// 5. Call `parent()` method to check if `text`'s parent is `element`.
    #[test]
    fn ut_text_parent() {
        let document = Document::new();
        let mut element = document.create_element("Element").unwrap();
        let text = document.create_text("Text").unwrap();
        assert_eq!(text.parent(), None);

        let _ = element.append_child(text.clone());
        assert_eq!(text.parent(), Some(element.as_parent_node()));
    }
}
